package server;

import java.io.IOException;
import java.net.Socket;
import java.util.InputMismatchException;

/**
 * @file Client_Handler.java
 *
 * @version 0.8
 * @author James Dibble 10009689
 *
 * Requires Java 7. Uses java.Files.readAllBytes()
 * 
 * Latest Source:
 * http://code.google.com/p/csp-server/source/browse/
 *                                  trunk/Server/src/server/Client_Handler.java
 */
public class Client_Handler implements Runnable {
    // <editor-fold defaultstate="collapsed" desc="Attributes">
    private Client_State state;
    private Socket sSocket;
    private Server parent;
    private Client_Response client;
    private Server_Response server;
    /**
     * 
     */
    public boolean bolDebug;
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Constructors">
    /**
     * Create a Client Handler that controls interactions via a Socket.
     *
     * @param sSocket
     * @param parent
     * @throws IOException
     */
    public Client_Handler(Socket sSocket, Server parent) throws IOException {
        this.state = Client_State.TERMS_OF_REFRENCE;
        this.sSocket = sSocket;
        this.parent = parent;
        this.server = new Server_Response(this.sSocket);
        this.client = new Client_Response(this.sSocket);
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Methods">
    @Override
    public void run() {
        try {
            server.SendToClient(
                              Server_Response.Client_Messages.TERMS_OF_SERVICE);
            String strClientInput;
            
            //Loop until the socket is closed abnormally, the client disconnects
            //or the client has requested to not reciecve any more files or
            //did not accept the terms.
            while ((strClientInput = client.getClientResponse()) != null
                    && sSocket.isConnected()
                    && state != Client_State.CLOSED) {
                
                //If verbose output is on display the contents of the last
                //stream from the client.
                if (Server.bolServerDebug) {
                    System.out.println(String.format("(%s): %s",
                            sSocket.getInetAddress().toString(),
                            strClientInput)
                    );
                }
                
                //What state is the client in?
                if (state == Client_State.TERMS_OF_REFRENCE) {
                    if (strClientInput.equalsIgnoreCase("y")) {
                        server.SendToClient(parent.listResources());
                        state = Client_State.FILE_LIST;
                    } else if (strClientInput.equalsIgnoreCase("n")) {
                        sSocket.close();
                        state = Client_State.CLOSED;
                    } else {
                        server.SendToClient(
                         Server_Response.Client_Messages.INVALID1.toString() +
                   Server_Response.Client_Messages.TERMS_OF_SERVICE.toString()
                        );
                    }
                    
                } else if (state == Client_State.FILE_LIST) {
                    try {
                        int intTemp = Integer.parseInt(strClientInput);
                        Resource thisResource = null;
                        
                        //Try and find the resource the client requested
                        if ((thisResource = parent.getResourceByID(intTemp)) 
                            != null) {
                            if (this.parent.getServerType() == 
                                Server.Server_Type.DESCRIPTION_SERVER) {
                                thisResource.UploadResourceAsString();
                                server.SendToClient(thisResource.toString());
                                System.out.println(
                                        String.format("(%s): Downloaded %s",
                                        sSocket.getInetAddress().toString(),
                                        thisResource.getFileName())
                                );
                            } else if (this.parent.getServerType() == 
                                       Server.Server_Type.FILE_SERVER) {
                                thisResource.UploadResourceAsFile();
                                server.SendResourceToClient(thisResource);
                                System.out.println(
                                        String.format("(%s): Downloaded %s",
                                        sSocket.getInetAddress().toString(),
                                        thisResource.getFileName())
                                );
                            }
                            server.SendToClient(
                              Server_Response.Client_Messages.ANOTHER.toString()
                            );
                            this.state = Client_State.ANOTHER;
                            
                        } else { //The client has requested an invalid file.
                            server.SendToClient(
                             Server_Response.Client_Messages.INVALID2.toString() 
                             + parent.listResources()
                            );
                        }
                    //The client has sent a non-numeric character.
                    } catch (InputMismatchException | 
                            NumberFormatException imEx) {
                        server.SendToClient(
                            Server_Response.Client_Messages.INVALID2.toString() 
                            + parent.listResources()
                        );
                    }
                    
                } else if (state == Client_State.ANOTHER) {
                    if (strClientInput.equalsIgnoreCase("y")) {
                        server.SendToClient(parent.listResources());
                        state = Client_State.FILE_LIST;
                    } else if (strClientInput.equalsIgnoreCase("n")) {
                        sSocket.close();
                        state = Client_State.CLOSED;
                    } else {
                        server.SendToClient(
                            Server_Response.Client_Messages.INVALID2.toString() 
                            + Server_Response.Client_Messages.ANOTHER.toString()
                        );
                    }
                }
            }
            System.out.println(String.format("(%s) has disconnected.",
                               sSocket.getInetAddress().toString()));
        //Socket has closed abnormally.
        } catch (IOException ex) {
            if (bolDebug) {
                ex.printStackTrace(System.out);
            }
            System.out.println(String.format("%s(%s) has disconnected.",
                               sSocket.getInetAddress().toString()));
        }
        Server.clients.remove(this);
        if(Server.bolServerDebug)
                     System.out.println("No Clients: " + Server.clients.size());
    }

    /**
     * Get the host name of the client. Using Windows on a network behind a
     * router implementing NAT, this will usually fail and return the IP of the
     * client.
     *
     * Issue:
     * http://code.google.com/p/csp-server/issues/
     *                                      detail?id=10&thanks=10&ts=1330471849
     * @return
     */
    public String getHostName() {
        return sSocket.getInetAddress().getHostName();
    }

    /**
     * Close all connections to this client.
     * @throws IOException 
     */
    public void Disconnect() throws IOException {
        sSocket.close();
    }
    // </editor-fold>

    /**
     * What state is the client in?
     */
    public enum Client_State {

        /**
         * The terms of reference have been sent and the server is awaiting 
         * response.
         */
        TERMS_OF_REFRENCE,
        /**
         *  The file list has been sent and the server is awaiting response.
         */
        FILE_LIST,
        /**
         *  The client has been asked if they want another file and the server
         * is awaiting a response.
         */
        ANOTHER,
        /**
         *  The client has requested to end the exchange.
         */
        CLOSED
    }

    
}